﻿using System;
using Spring.Data.NHibernate;
using NHibernate.Cfg;
using NHibernate.Mapping.ByCode;
using System.Reflection;
using NHibernate.Cfg.MappingSchema;
using NHibernate.Tool.hbm2ddl;
using Domain.Model.Commons;
using Domain.Model.Commons.Mapping;

namespace Domain.Repository
{
    public class ByCodeLocalSessionFactoryObject : LocalSessionFactoryObject
    {
        private string[] newMappingAssemblies;

        /// <summary>
        /// PostProcessMappings
        /// </summary>
        /// <param name="config"></param>
        protected override void PostProcessMappings(Configuration config)
        {
            if (null != this.newMappingAssemblies && this.newMappingAssemblies.Length > 0)
            {
                var mapper = new ModelMapper();
                foreach (string entry in newMappingAssemblies)
                {
                    log.Info("=====>>Load libary : " + entry);
                    Type[] types = Assembly.Load(entry).GetExportedTypes();
                    if (null != types && types.Length > 0)
                    {
                        foreach (Type item in types)
                        {
                            Type mappingType = null;
                            Type genericType = null;

                            CommonTools.AddModel(item);

                            InheritMappingHelper mappingHelper = new InheritMappingHelper();
                            genericType = mappingHelper.ResolveMappingType(item);
                            if (null != genericType)
                            {
                                AddMapping(mapper, genericType);
                                continue;
                            }
                            if (item.IsSubclassOf(typeof(BaseEntity)))
                            {
                                mappingType = typeof(BaseEntityMapping<>);
                                genericType = mappingType.MakeGenericType(new Type[] { item });
                                AddMapping(mapper, genericType);
                                continue;
                            }
                            if (item.IsSubclassOf(typeof(AbstractEntity)))
                            {
                                mappingType = typeof(EntityMapping<>);
                                genericType = mappingType.MakeGenericType(new Type[] { item });
                                AddMapping(mapper, genericType);
                                continue;
                            }
                            if (item.IsSubclassOf(typeof(AbstractDictEntity)))
                            {
                                mappingType = typeof(DictEntityMapping<>);
                                genericType = mappingType.MakeGenericType(new Type[] { item });
                                AddMapping(mapper, genericType);
                                continue;
                            }
                            if (item.IsSubclassOf(typeof(AbstractComponent)))
                            {
                                mappingType = typeof(ComponentMapping<>);
                                genericType = mappingType.MakeGenericType(new Type[] { item });
                                AddMapping(mapper, genericType);
                                continue;
                            }
                        }
                    }
                }

                //mapper.AddMapping(new EntityMapping<PersonInfo>());
                //mapper.AddMapping(new BaseEntityMapping<Enterprise>());

                HbmMapping bycodeMapping = mapper.CompileMappingForAllExplicitlyAddedEntities();
                config.AddMapping(bycodeMapping);

                RefreshSchema(config);

                log.Info("=====>> Mapping count : " + config.ClassMappings.Count);
                //log.Info("=====>>Class mapping PersonInfo : " + config.GetClassMapping((typeof(PersonInfo).ToString())));
                log.Info("=====>>Mapping String : \n"+bycodeMapping.AsString());
                
            }
        }

        private void AddMapping(ModelMapper mapper, Type genericType)
        {
            //var mappingInstance = Activator.CreateInstance(genericType);
            //mapper.GetType().GetMethod("AddMapping").Invoke(mapper, new object[] { genericType });

            mapper.AddMapping(genericType);
        }

        protected void RefreshSchema(Configuration config)
        {
            SchemaUpdate instance = new SchemaUpdate(config);
            instance.Execute(true, true);
        }

        public new string[] MappingAssemblies
        {
            set
            {
                this.newMappingAssemblies = value;
                base.MappingAssemblies = this.newMappingAssemblies;
            }
        }
    }
}
